# File name   : Makefile
# Author      : longbin
# Created date: 2022-12-02 10:55:48
#

PLATFORM := linux
_this_ := $(notdir $(CURDIR))_$(shell /bin/bash -c 'echo $${RANDOM}')
$(_this_)_target_name := $(notdir $(CURDIR))
$(_this_)_target_type := bin

$(_this_)_path := $(CURDIR)
$(_this_)_src_root_path = $($(_this_)_path)

$(_this_)_build_path := $($(_this_)_path)/build
$(_this_)_release_path := $($(_this_)_build_path)/release
$(_this_)_obj_path := $($(_this_)_build_path)/$($(_this_)_target_type)

## set target name, eg: libx.so, liby.a, example(bin).
ifeq ($($(_this_)_target_type),so)
  $(_this_)_target := $($(_this_)_obj_path)/lib$($(_this_)_target_name).so
else ifeq ($($(_this_)_target_type),a)
  $(_this_)_target := $($(_this_)_obj_path)/lib$($(_this_)_target_name).a
else ifeq ($($(_this_)_target_type),bin)
  $(_this_)_target := $($(_this_)_obj_path)/$($(_this_)_target_name)
endif

ignore := $(shell [ -d $($(_this_)_obj_path) ] || mkdir -p $($(_this_)_obj_path))

ifeq ($(PLATFORM),qnx)
  ## license check is required for qcc command
  # CC = qcc -Vgcc_ntoaarch64le_gpp
  # XX = qcc -Vgcc_ntoaarch64le_gpp
  # AR = qcc -Vgcc_ntoaarch64le_gpp

  CC = aarch64-unknown-nto-qnx7.1.0-gcc
  XX = aarch64-unknown-nto-qnx7.1.0-g++
  AR = aarch64-unknown-nto-qnx7.1.0-ar

  CFLAGS = -D_QNX_SOURCE=1 -DQNX_NTO
  ifeq ($(XX),aarch64-unknown-nto-qnx7.1.0-g++)
    CXXFLAGS += -stdlib=libstdc++
  endif

  LDFLAGS += -Wl,-Bdynamic -lc++ -lstdc++ -lbacktrace -pthread -rdynamic
  LDFLAGS += -lscreen

else
  CC = gcc
  XX = g++
  AR = ar

  CFLAGS = -D_QNX_SOURCE=0

  # LDFLAGS += -Wl,--start-group -Wl,-Bstatic -lmystaticlib -Wl,--end-group
  LDFLAGS += -Wl,-Bdynamic -lstdc++ -lbacktrace -pthread -rdynamic
  # LDFLAGS += -lGL -lrt -lXi -lX11 -lpthread -lXrandr -lXinerama -lXcursor

  LDFLAGS += -lpthread -ldl -lm

  DBGFLAGS := -fsanitize=address -fno-omit-frame-pointer
endif

CFLAGS += -fPIC -fasynchronous-unwind-tables -funwind-tables -fstack-protector-all -Wunused-variable -Wreturn-type -Wundef -Wextra -Wno-unused-parameter -Wmissing-field-initializers -Wunused -Wunreachable-code -Wtrampolines -Wconversion -Wsign-conversion -Wformat-security -Werror=return-type -g -O2
CFLAGS += -Werror -Werror=return-type -Wall
CXXFLAGS = -lang-c++ -std=c++11 $(CFLAGS) -fexceptions -fnon-call-exceptions

NO_RVO = -fno-elide-constructors
#CXXFLAGS += $(NO_RVO)

# LDFLAGS = -L../lib
# LDFLAGS += -Wl,--start-group -Wl,-Bstatic -lmystaticlib1 -lmystaticlib2 -Wl,--end-group

CXXFLAGS += $(DBGFLAGS)
LDFLAGS += $(DBGFLAGS)

# INCLUDES = -I../include

$(_this_)_all_sub_dirs := $(shell find $($(_this_)_src_root_path) -maxdepth 8 -type d)
$(_this_)_inc_dirs := $(filter %include %_inc, $($(_this_)_all_sub_dirs))
$(_this_)_src_dirs := $(filter %source  %_src, $($(_this_)_all_sub_dirs))
$(_this_)_lib_dirs := $(filter %lib %_lib, $($(_this_)_all_sub_dirs))

$(_this_)_inc_dirs += $($(_this_)_path)
$(_this_)_src_dirs += $($(_this_)_path)
$(_this_)_lib_dirs += $($(_this_)_path)

# get all source files and libs
$(_this_)_c_source_files := $(foreach src_dir,$($(_this_)_src_dirs),$(wildcard $(src_dir)/*.c))
$(_this_)_cpp_source_files := $(foreach src_dir,$($(_this_)_src_dirs),$(wildcard $(src_dir)/*.cpp))
$(_this_)_bin_static_libs := $(foreach lib_dir, $($(_this_)_lib_dirs), $(wildcard $(lib_dir)/*.a))
$(_this_)_bin_shared_libs := $(foreach lib_dir, $($(_this_)_lib_dirs), $(wildcard $(lib_dir)/*.so))

# get all object files and rules(target and dependency)
$(_this_)_c_objs := $(addprefix $($(_this_)_obj_path)/, $(notdir $(patsubst %.c, %.o, $($(_this_)_c_source_files))))
$(_this_)_cpp_objs := $(addprefix $($(_this_)_obj_path)/, $(notdir $(patsubst %.cpp, %.o, $($(_this_)_cpp_source_files))))
$(_this_)_c_obj_rules := $(join $(addsuffix :,$($(_this_)_c_objs)),$($(_this_)_c_source_files))
$(_this_)_cpp_obj_rules := $(join $(addsuffix :,$($(_this_)_cpp_objs)),$($(_this_)_cpp_source_files))

# generate all objects and build flags
$(_this_)_all_objects := $(strip $($(_this_)_c_objs) $($(_this_)_cpp_objs))
$(_this_)_inc_flags := $(patsubst %,-I%,$($(_this_)_inc_dirs))
$(_this_)_lib_flags := $(patsubst %,-L%,$($(_this_)_lib_dirs))

# append current module include dirs and libs to compile flags.
INCLUDES += $($(_this_)_inc_flags)
LDFLAGS  += $($(_this_)_lib_flags)

# VERBOSE_MAKE = 1
ifdef VERBOSE_MAKE
  $(warning "$(_this_) CC: $(CC)")
  $(warning "$(_this_) XX: $(XX)")
  $(warning "$(_this_) AR: $(AR)")
  $(warning "$(_this_) platform: $(PLATFORM)")
  $(warning "$(_this_) target: $($(_this_)_target)")
  $(warning "$(_this_) target type: $($(_this_)_target_type)")
  $(warning "$(_this_) work path: $($(_this_)_path)")
  $(warning "$(_this_) sub dirs: $($(_this_)_all_sub_dirs)")
  $(warning "$(_this_) inc dirs: $($(_this_)_inc_dirs)")
  $(warning "$(_this_) src dirs: $($(_this_)_src_dirs)")
  $(warning "$(_this_) lib dirs: $($(_this_)_lib_dirs)")
  $(warning "$(_this_) bin static libs: $($(_this_)_bin_static_libs)")
  $(warning "$(_this_) bin shared libs: $($(_this_)_bin_shared_libs)")
  $(warning "$(_this_) c objs: $($(_this_)_c_objs)")
  $(warning "$(_this_) cpp objs: $($(_this_)_cpp_objs)")
  $(warning "$(_this_) all objs: $($(_this_)_all_objects)")
  $(warning "$(_this_) inc flags: $(INCLUDES)")
  $(warning "$(_this_) ld flags: $(LDFLAGS)")
  $(warning "$(_this_) c rules: $($(_this_)_c_obj_rules)")
  $(warning "$(_this_) cpp rules: $($(_this_)_cpp_obj_rules)")
  DEBUG =
else
  DEBUG = @
endif

ifeq ($(strip $($(_this_)_all_objects)),)
  $(warning "empty objects")
  $(error "empty objects")
endif

all: $($(_this_)_target)

# define compile rule for shared lib(so), static lib(a), excutable target(bin).
ifeq ($($(_this_)_target_type),so)
$($(_this_)_target): $($(_this_)_all_objects)
	@echo "building shared library: $($(_this_)_target)"
	$(DEBUG)$(XX) -shared -o $@ $^ $(LDFLAGS)
else ifeq ($($(_this_)_target_type),a)
$($(_this_)_target): $($(_this_)_all_objects)
	@echo "building static library: $($(_this_)_target)"
	$(DEBUG)$(AR) -r $@ $^
else ifeq ($($(_this_)_target_type),bin)
$($(_this_)_target): $($(_this_)_all_objects)
	@echo "building bin object: $($(_this_)_target)"
	$(DEBUG)$(XX) -o $@ $^ $(LDFLAGS)
endif

# generate object rules
define make_compile_c_rule
$(1)
	@echo "compiling($(PLATFORM) $(CC)): $$^"
	$(DEBUG)$(CC) $(CFLAGS) $(INCLUDES) -c $$^ -o $$@
endef

define make_compile_cpp_rule
$(1)
	@echo "compiling($(PLATFORM) $(XX)): $$^"
	$(DEBUG)$(XX) $(CXXFLAGS) $(INCLUDES) -c $$^ -o $$@
endef

$(foreach _rule_, $($(_this_)_c_obj_rules),\
  $(eval $(call make_compile_c_rule,$(_rule_))))

$(foreach _rule_, $($(_this_)_cpp_obj_rules),\
  $(eval $(call make_compile_cpp_rule,$(_rule_))))

.phony: clean help release
release: $($(_this_)_target)
	@echo "release to $($(_this_)_release_path)"
	$(shell [ -d $($(_this_)_release_path) ] || mkdir -p $($(_this_)_release_path))
	@cp $($(_this_)_target) $($(_this_)_release_path)
	@#cp -rfd include $($(_this_)_release_path)

help:
	@echo "Usages:"
	@echo "  make clean          to clean project"
	@echo "  make release        to release project"
	@echo "  make all            to build all objects"

clean:
	-rm -rfv $($(_this_)_target)
	-rm -rfv $($(_this_)_all_objects)
	-rm -rfv $($(_this_)_obj_path)
	-rm -rfv $($(_this_)_release_path)
	-rm -rfv $($(_this_)_build_path)

